package com.kinyx.framework.business;

import java.security.SecureRandom;

import javax.math.YBigDecimal;

import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.annotation.assist.ReturnNull;
import com.kinyx.framework.annotation.assist.Throws;
import com.kinyx.framework.enums.impl.ErrorCode0;
import com.kinyx.framework.enums.impl.ErrorCodeX;
import com.kinyx.framework.enums.impl.RandomRange;
import com.kinyx.framework.exception.KRuntimeException;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 随机工具类
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class KRandomUtils {

	/** 随机数 */
	private static final SecureRandom RANDOM = new SecureRandom();

	/**
	 * 随机整数
	 * @param min 最小值
	 * @param max 最大值
	 * @return 整数
	 */
	@ReturnNotNull
	private static int nextInt(@CanNotNull final int min, @CanNotNull final int max) {
		return min + RANDOM.nextInt((max + 1) - min);
	}

	/**
	 * 随机数值
	 * @param min 最小值
	 * @param max 最大值
	 * @return 数值
	 */
	@ReturnNotNull
	@Throws(at = "KNumberUtils.toBigDecimal(Object)", value = "ErrorCode0.CanNotConvertType", when = "该类型不可转换")
	private static YBigDecimal nextKBD(@CanNotNull final Object min, @CanNotNull final Object max) {
		// FIXME 在取得小于值前的概率分布并不均匀，例如：diff==10的情况下， 该算法在第一位取得1的概率是50%，那么第二位就只能是0，则10出现的概率将会是总体结果的50%，而均匀分布的情况下，10出现的概率应为1/11（0-10共11个值）。
		final String diff = YBigDecimal.of(max).subtract(min).toString();
		final StringBuilder sb = new StringBuilder();
		boolean flag = false;
		for (int i = 0; i < diff.length(); i++) {
			final String charI = diff.substring(i, i + 1);
			if (charI.equals(".")) { sb.append("."); continue; }
			if (flag) {
				sb.append(nextInt(0, 9));
			} else {
				final int valueI = Integer.parseInt(charI);
				final int valueR = nextInt(0, valueI);
				sb.append(valueR);
				if (valueR < valueI) {
					flag = true;
				}
			}
		}
		return YBigDecimal.of(min).add(sb.toString());
	}

	/**
	 * 随机boolean值
	 * @return boolean值
	 */
	@ReturnNotNull
	private static boolean nextBoolean() { return RANDOM.nextBoolean(); }

	/**
	 * 随机char值
	 * @param min 最小值
	 * @param max 最大值
	 * @return char值
	 */
	@ReturnNotNull
	@Throws(at = "KRandomUtils.random(char, char)", value = "ErrorCode0.MinValueMoreThenMaxValue", when = "最小值大于最大值")
	public static char random(@CanNotNull final char min, @CanNotNull final char max) {
		if (max < min) {
			throw new KRuntimeException(ErrorCode0.MinValueMoreThenMaxValue).param("minValue", min).param("maxValue", max);
		}
		if (min == max) { return min; }
		return (char) nextInt(min, max);
	}

	/**
	 * 随机byte值
	 * @param min 最小值
	 * @param max 最大值
	 * @return byte值
	 */
	@ReturnNotNull
	@Throws(at = "KRandomUtils.random(byte, byte)", value = "ErrorCode0.MinValueMoreThenMaxValue", when = "最小值大于最大值")
	public static byte random(@CanNotNull final byte min, @CanNotNull final byte max) {
		if (max < min) {
			throw new KRuntimeException(ErrorCode0.MinValueMoreThenMaxValue).param("minValue", min).param("maxValue", max);
		}
		if (min == max) { return min; }
		return (byte) nextInt(min, max);
	}

	/**
	 * 随机short值
	 * @param min 最小值
	 * @param max 最大值
	 * @return short值
	 */
	@ReturnNotNull
	@Throws(at = "KRandomUtils.random(short, short)", value = "ErrorCode0.MinValueMoreThenMaxValue", when = "最小值大于最大值")
	public static short random(@CanNotNull final short min, @CanNotNull final short max) {
		if (max < min) {
			throw new KRuntimeException(ErrorCode0.MinValueMoreThenMaxValue).param("minValue", min).param("maxValue", max);
		}
		if (min == max) { return min; }
		return (short) nextInt(min, max);
	}

	/**
	 * 随机int值
	 * @param min 最小值
	 * @param max 最大值
	 * @return int值
	 */
	@ReturnNotNull
	@Throws(at = "KRandomUtils.random(int, int)", value = "ErrorCode0.MinValueMoreThenMaxValue", when = "最小值大于最大值")
	public static int random(@CanNotNull final int min, @CanNotNull final int max) {
		if (max < min) {
			throw new KRuntimeException(ErrorCode0.MinValueMoreThenMaxValue).param("minValue", min).param("maxValue", max);
		}
		if (min == max) { return min; }
		if (((max > 0) && ((max + 1) < 0)) || (((max + 1) - min) < 0)) {// 精度越界
			// 平均分成三段，随机从其中一段中随机取值。
			final int divide = YBigDecimal.of(max).subtract(min).divide(3).intValue();
			final int first = YBigDecimal.of(min).add(divide).intValue();
			final int second = YBigDecimal.of(first).add(divide).intValue();
			switch (nextInt(0, 2)) {
				case 0:
					return nextInt(min, first);
				case 1:
					return nextInt(first + 1, second);
				case 2:
					return nextInt(second + 1, max);
				default :
					throw new KRuntimeException(ErrorCodeX.NeverHappens);
			}
		}
		return nextInt(min, max);
	}

	/**
	 * 随机long值
	 * @param min 最小值
	 * @param max 最大值
	 * @return long值
	 */
	@ReturnNotNull
	@Throws(at = "KRandomUtils.random(long, long)", value = "ErrorCode0.MinValueMoreThenMaxValue", when = "最小值大于最大值")
	public static long random(@CanNotNull final long min, @CanNotNull final long max) {
		if (max < min) {
			throw new KRuntimeException(ErrorCode0.MinValueMoreThenMaxValue).param("minValue", min).param("maxValue", max);
		}
		if (min == max) { return min; }
		return nextKBD(min, max).longValue();
	}

	/**
	 * 随机float值
	 * @param min 最小值
	 * @param max 最大值
	 * @return float值
	 */
	@ReturnNotNull
	@Throws(at = "KRandomUtils.random(float, float)", value = "ErrorCode0.MinValueMoreThenMaxValue", when = "最小值大于最大值")
	public static float random(@CanNotNull final float min, @CanNotNull final float max) {
		if (max < min) {
			throw new KRuntimeException(ErrorCode0.MinValueMoreThenMaxValue).param("minValue", min).param("maxValue", max);
		}
		if (min == max) { return min; }
		return nextKBD(min, max).floatValue();
	}

	/**
	 * 随机double值
	 * @param min 最小值
	 * @param max 最大值
	 * @return double值
	 */
	@ReturnNotNull
	@Throws(at = "KRandomUtils.random(double, double)", value = "ErrorCode0.MinValueMoreThenMaxValue", when = "最小值大于最大值")
	public static double random(@CanNotNull final double min, @CanNotNull final double max) {
		if (max < min) {
			throw new KRuntimeException(ErrorCode0.MinValueMoreThenMaxValue).param("minValue", min).param("maxValue", max);
		}
		if (min == max) { return min; }
		return nextKBD(min, max).doubleValue();
	}

	/**
	 * 随机Boolean值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @return Boolean值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static Boolean randomBoolean(@CanNotNull final boolean nullable) { return nullable && nextBoolean() ? null : nextBoolean(); }

	/**
	 * 随机Character值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @return Character值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static Character randomCharacter(@CanNotNull final boolean nullable) { return nullable && nextBoolean() ? null : random(Character.MIN_VALUE, Character.MAX_VALUE); }

	/**
	 * 随机Byte值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @return Byte值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static Byte randomByte(@CanNotNull final boolean nullable) { return nullable && nextBoolean() ? null : random(Byte.MIN_VALUE, Byte.MAX_VALUE); }

	/**
	 * 随机Short值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @return Short值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static Short randomShort(@CanNotNull final boolean nullable) { return nullable && nextBoolean() ? null : random(Short.MIN_VALUE, Short.MAX_VALUE); }

	/**
	 * 随机Integer值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @return Integer值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static Integer randomInteger(@CanNotNull final boolean nullable) { return nullable && nextBoolean() ? null : random(Integer.MIN_VALUE, Integer.MAX_VALUE); }

	/**
	 * 随机Long值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @return Long值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static Long randomLong(@CanNotNull final boolean nullable) { return nullable && nextBoolean() ? null : random(Long.MIN_VALUE, Long.MAX_VALUE); }

	/**
	 * 随机Float值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @return Float值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static Float randomFloat(@CanNotNull final boolean nullable) { return nullable && nextBoolean() ? null : random(Float.MIN_VALUE, Float.MAX_VALUE); }

	/**
	 * 随机Double值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @return Double值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static Double randomDouble(@CanNotNull final boolean nullable) { return nullable && nextBoolean() ? null : random(Double.MIN_VALUE, Double.MAX_VALUE); }

	/**
	 * 随机String值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @param length 字符串长度
	 * @param range 取值范围
	 * @return String值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static String randomString(@CanNotNull final boolean nullable, final int length, @CanNotNull final String range) {
		if (nullable && nextBoolean()) { return null; }
		final int min = 0;
		final int max = range.length() - 1;
		final char[] result = new char[length];
		for (int i = 0; i < result.length; i++) {
			result[i] = range.charAt(random(min, max));
		}
		return new String(result);
	}

	/**
	 * 随机String值
	 * @param nullable true：随机值允许为null；false：随机值不允许为null；
	 * @param length 字符串长度
	 * @param range 取值范围
	 * @return String值
	 */
	@ReturnNull("nullable == true时随机值可能为null")
	public static String randomString(@CanNotNull final boolean nullable, final int length, @CanNotNull final RandomRange range) {
		return randomString(nullable, length, range.getRange());
	}

}
